/*
 *	Copyright (C) 2003-2006 Gabest
 *	http://www.gabest.org
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#pragma once

namespace ssf
{
// simple array class for simple types without constructors,
// and it doesn't free its reserves on SetCount(0)

template<class T>
class Array
{
    T* m_pData;
    size_t m_nSize;
    size_t m_nMaxSize;
    size_t m_nGrowBy;

public:
    Array()
    {
        m_pData = NULL;
        m_nSize = m_nMaxSize = 0;
        m_nGrowBy = 4096;
    }
    virtual ~Array()
    {
        if(m_pData) _aligned_free(m_pData);
    }

    void SetCount(size_t nSize, size_t nGrowBy = 0)
    {
        if(nGrowBy > 0)
        {
            m_nGrowBy = nGrowBy;
        }

        if(nSize > m_nMaxSize)
        {
            m_nMaxSize = nSize + max(m_nGrowBy, m_nSize);
            size_t nBytes = m_nMaxSize * sizeof(T);
            m_pData = m_pData ? (T*)_aligned_realloc(m_pData, nBytes, 16) : (T*)_aligned_malloc(nBytes, 16);
        }

        m_nSize = nSize;
    }

    size_t GetCount() const
    {
        return m_nSize;
    }

    void RemoveAll()
    {
        m_nSize = 0;
    }
    bool IsEmpty() const
    {
        return m_nSize == 0;
    }

    T* GetData()
    {
        return m_pData;
    }

    void Add(const T& t)
    {
        size_t nPos = m_nSize;
        SetCount(m_nSize + 1);
        m_pData[nPos] = t;
    }

    void Append(const Array& a, size_t nGrowBy = 0)
    {
        Append(a.m_pData, a.m_nSize, nGrowBy);
    }

    void Append(const T* ptr, size_t nSize, size_t nGrowBy = 0)
    {
        if(!nSize) return;
        size_t nOldSize = m_nSize;
        SetCount(nOldSize + nSize);
        memcpy(m_pData + nOldSize, ptr, nSize * sizeof(T));
    }

    const T& operator [](size_t i) const
    {
        return m_pData[i];
    }
    T& operator [](size_t i)
    {
        return m_pData[i];
    }

    void Copy(const Array& v)
    {
        SetCount(v.GetCount());
        memcpy(m_pData, v.m_pData, m_nSize * sizeof(T));
    }

    void Move(Array& v)
    {
        Swap(v);
        v.SetCount(0);
    }

    void Swap(Array& v)
    {
        T* pData = m_pData;
        m_pData = v.m_pData;
        v.m_pData = pData;
        size_t nSize = m_nSize;
        m_nSize = v.m_nSize;
        v.m_nSize = nSize;
        size_t nMaxSize = m_nMaxSize;
        m_nMaxSize = v.m_nMaxSize;
        v.m_nMaxSize = nMaxSize;
        size_t nGrowBy = m_nGrowBy;
        m_nGrowBy = v.m_nGrowBy;
        v.m_nGrowBy = nGrowBy;
    }
};
}